/**
 * 
 */
package co.fitstart.entity.course.round;

import java.beans.Transient;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.persistence.AttributeOverride;
import javax.persistence.AttributeOverrides;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Embedded;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.MapKey;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.OrderBy;
import javax.persistence.Table;

import org.apache.commons.collections.MapUtils;
import org.hibernate.annotations.DynamicUpdate;
import org.hibernate.annotations.Where;

import com.alibaba.fastjson.annotation.JSONField;
import com.fasterxml.jackson.annotation.JsonIdentityInfo;
import com.fasterxml.jackson.annotation.JsonIdentityReference;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.ObjectIdGenerators;

import co.fitstart.entity.Constant;
import co.fitstart.entity.course.Course;
import co.fitstart.entity.course.CourseType;
import co.fitstart.entity.course.CourseUnit;
import co.fitstart.entity.course.schedule.CourseSchedule;
import co.fitstart.entity.courseware.fitnessgroup.BatchCourseware;
import co.fitstart.entity.courseware.fitnessgroup.FitnessGroup;
import co.fitstart.entity.order.Order;
import co.fitstart.entity.principal.Principal;
import co.fitstart.entity.product.Product;
import co.fitstart.entity.site.Site;
import co.fitstart.entity.user.Student;
import co.fitstart.entity.utils.DateFormatUtils;
import co.fitstart.entity.utils.DateUtils;
import co.fitstart.entity.utils.StringUtils;

/**
 * 课次
 * 
 * @author downpour
 *
 */
@Entity
@Table(name = "course_round")
@DynamicUpdate
@JsonIgnoreProperties(value={"hibernate_lazy_initializer","handler","fieldHandler"})
public class CourseRound {
    
    @Id
    @GeneratedValue
    private Long id;

    /** 课程编号 */
    private String code;
    
    /** 所属课程 */
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
    @JsonIdentityReference(alwaysAsId=true)
    private Course course;
    
    /** 课程类型 (便于后台列表功能-------类型+课程状态能区分大部分课程列表) */
    @Enumerated(EnumType.ORDINAL)
    private CourseType courseType;
    
    @Enumerated(EnumType.STRING)
    private CourseUnit courseUnit;
    
    /** 所属门店 线下订单 */
    @ManyToOne(fetch = FetchType.LAZY, optional = true)
    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
    @JsonIdentityReference(alwaysAsId=true)
    private Site site;
    
    /** 所属产品 */
    @ManyToOne(fetch = FetchType.LAZY, optional = true)
    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
    @JsonIdentityReference(alwaysAsId=true)
    private Product product;
    
    /** 订单 */
    @ManyToOne(fetch = FetchType.LAZY, optional = true)
    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
    @JsonIdentityReference(alwaysAsId=true)
    private Order order;
    
    /** 学员 */
    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
    @JsonIdentityReference(alwaysAsId=true)
    private Student student;
    
    /** 教练 */
    @ManyToOne(fetch = FetchType.LAZY, optional = true)
    @JSONField(serialize = false)
    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
    @JsonIdentityReference(alwaysAsId=true)
    private Principal coach;
    
    /** 所属分组 */
    @ManyToOne(fetch = FetchType.LAZY, optional = true)
    @JSONField(serialize = false)
    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
    @JsonIdentityReference(alwaysAsId=true)
    private FitnessGroup fitnessGroup;
    
    /** 课件分组状态 */
    @Enumerated(EnumType.ORDINAL)
    private RoundCoursewareStatus roundCoursewareStatus;
    
    /** 创建时间 */
    @JSONField(serialize = false)
    private Date createTime;
    
    /** 开始时间 (第一次排期时间) */
    @JSONField(serialize = false)
    private Date effectiveStart;
    
    /** 结束时间 (第一次开始时间+课程最长周期) */
    
    @JSONField(serialize = false)
    private Date effectiveEnd;
    
    /** 年卡冻结时间 */
    @JSONField(serialize = false)
    private Date holdDate;
    
    /** 年卡冻结结束时间 */
    @JSONField(serialize = false)
    private Date holdEndDate;
    
    /** 结营时间 */
    @JSONField(serialize = false)
    private Date finishTime;
    
    /** 状态 */
    @Enumerated(EnumType.ORDINAL)
    private RoundStatus courseStatus;
    
    /** 已完成节次数*/
    private int finished;
    
    /** 已安排课时课件数 */
    private int plannedCourse;
    
    /** 已安排每周课件数 */
    private int plannedWeekly;
    
    /** 已安排训练部位 */
    private int plannedContent;
    
    /** 已安排上课课件 */
    private int plannedCourseware;
    
    /** 已冻结次数 */
    private int freezedNumber;
    
    /** 是否赠送课程 */
    private boolean given;
    
    /** 是否是该用户的最后一节课 */
    private Boolean lastCourse;
    
    private String courseTags;
    
    /** 团课冗余字段 */
    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "description", column = @Column(name = "description")),
        @AttributeOverride(name = "gym", column = @Column(name = "gym")),
        @AttributeOverride(name = "openDescription", column = @Column(name = "open_description"))
    })
    private GroupRoundAttr groupAttr;
    
    /** 线上课程冗余字段 */
    @Embedded
    @AttributeOverrides({
        @AttributeOverride(name = "bmiValue", column = @Column(name = "bmi_value")),
        @AttributeOverride(name = "zipName", column = @Column(name = "zip_name")),
        @AttributeOverride(name = "qrcodeStatus", column = @Column(name = "qrcode_status")),
        @AttributeOverride(name = "oldStudent", column = @Column(name = "old_student")),
        @AttributeOverride(name = "onlineRoundGroupStatus", column = @Column(name = "online_round_group_status"))
    })
    private OnlineRoundAttr onlineRoundAttr;
    
    /** 场次课程表 */
    @OneToMany(mappedBy = "courseRound", fetch = FetchType.LAZY)
    @Where(clause = "is_disabled = 0")
    @OrderBy("schedule_status DESC, occur_date ASC, occur_hour ASC")
    @JSONField(serialize = false)
    @JsonIgnore
    private List<CourseSchedule> schedules;
    
    /** 线上计划 */
    @OneToMany(mappedBy = "courseRound", fetch = FetchType.LAZY)
    @Where(clause = "is_disabled = 0 AND type = 0")
    @OrderBy("sorting ASC")
    @JSONField(serialize = false)
    @JsonIgnore
    private List<BatchCourseware> batches;
    
    /** 场次属性 */
    @OneToMany(mappedBy = "courseRound", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @MapKey(name = "name")
    @JSONField(serialize = false)
    @JsonIgnore
    private Map<String, CourseRoundAttribute> attrs;
    
    @Column(name = "is_disabled")
    private boolean disabled;
    
    /** 是否填写目标数据 */
    private Boolean fullInTarget;
    
    /** 小程序成就分享二维码 */
    private String achievementQrcode;
    
    /** 主课程 */
    @OneToOne(fetch = FetchType.LAZY, optional = true)
    @JSONField(serialize = false)
    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
    @JsonIdentityReference(alwaysAsId=true)
    private CourseRound mainCourseRound;
    
    /** 赠课 */
    @OneToOne(mappedBy = "mainCourseRound", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
    @JSONField(serialize = false)
    @JsonIdentityInfo(generator=ObjectIdGenerators.PropertyGenerator.class, property="id")
    @JsonIdentityReference(alwaysAsId=true)
    private CourseRound giveCourseRound;
    
    /**
     * default constructor
     */
    public CourseRound() {
    }
    
    
    
    /**
     * 
     * @param student
     * @param order
     */
    public CourseRound(Student student, Order order) {
        
        this.student = student;
        this.order = order;
        this.product = order.getProduct();
        this.createTime = new Date();
        this.courseStatus = RoundStatus.PREPARE;
        this.roundCoursewareStatus = RoundCoursewareStatus.GROUPING;
        this.disabled = false;
        this.given = false;
        
        // create order attr
        this.addAttribute(Constant.COURSER_ROUND_ORDER, order.getId().toString());
        
    }
    
    /**
     * 
     * @param student
     * @param product
     */
    public CourseRound(Student student, Product product) {
        this.student = student;
        this.product = product;
        this.createTime = new Date();
        this.courseStatus = RoundStatus.PREPARE;
        this.roundCoursewareStatus = RoundCoursewareStatus.GROUPING;
        this.disabled = false;
        this.given = true;
    }
    
    /**
     * initialize with course and site ( available when it is an offline order )
     * 
     * @param course
     * @param site
     * @return
     */
    public CourseRound initialize(Course course, Site site) {

        // init course and course type
        this.course = course;
        this.courseType = course.decide();
        
        // add attribute of course amount, and this is the real calulate hours
        this.addAttribute("course_amount", course.getHours());
        
        if(this.course.isOffline()) {
            this.site = site;
        }
        
        this.code = this.generateCode();
        
        this.onlineRoundAttr = new OnlineRoundAttr();
        onlineRoundAttr.initilization(this.courseType, this.student.getStat().getOnlineCount());
        
        return this;
    }
    
    public CourseRound initGivenRound(Course course, Site site) {

        // init course and course type
        this.course = course;
        this.courseType = course.decide();
        
        // add attribute of course amount, and this is the real calulate hours
        this.addAttribute("course_amount", this.generateHours(course));
        
        if(this.course.isOffline()) {
            this.site = site;
        }
        
        this.code = this.generateCode();
        
        this.onlineRoundAttr = new OnlineRoundAttr();
        onlineRoundAttr.initilization(this.courseType, this.student.getStat().getOnlineCount());
        
        return this;
    }
    
    public List<String> getCourseTagList() {
    	if(org.apache.commons.lang3.StringUtils.isEmpty(this.courseTags)) {
    		return null;
    	}
    	String[] tags = StringUtils.split(this.courseTags, ",");
    	List<String> results = new ArrayList<String>();
    	for(String tag : tags) {
    		if(org.apache.commons.lang3.StringUtils.equalsIgnoreCase(tag, "vipr_special")) {
    			results.add("VIPR");
    		} else if(org.apache.commons.lang3.StringUtils.equalsIgnoreCase(tag, "plt_special")) {
    			results.add("普拉提");
    		} else {
    			CourseUnit courseUnit = CourseUnit.aliasOf(tag);
        		if(courseUnit != null) {
        			results.add(courseUnit.getDescription());
        		}
    		}
    	}
    	
    	return results;
    }
    
    /**
     * 
     * @param course
     * @return
     */
    private int generateHours(Course course) {
        if(course.isYear()) {
            return 12;
        } else {
            int hours = course.getHours(), j = 0;
            if(hours >= 12 && hours <= 36) {
                j = 1;
            } else if (hours >= 48 && hours <= 72) {
                j = 2;
            } else if (hours >= 84 && hours <= 108) {
                j = 3;
            }
            return hours/12 + j;
        }
    }

    /**
     * 
     * @return
     */
    private String generateCode() {
        String code = this.courseType.getShortName() + this.product.getId().toString() + this.course.getId();
        
        if(this.courseType.isGroup()) { // null group course round code 
            return null;
            
        }else if(this.courseType.isOnline()) {  //  CourseType + ProductId + CourseId
            return code;
            
        }else {
            return code + this.createTime.getTime(); // CourseType + ProductId + CourseId + timestamp
        }
    }
    
    /**
     * 
     * @param site
     * @param coach
     * @return
     */
    public CourseRound changeSite(Site site, Principal coach) {
        this.site = site;
        this.coach = coach;
        return this;
    }
    
    /**
     * 
     * @param student
     * @return
     */
    public boolean matches(Student student) {
        return this.student.getId().equals(student.getId());
    }
    
    /**
     * 
     * @param courseStatus
     * @return
     */
    public CourseRound changeStatus(RoundStatus courseStatus) {
        this.courseStatus = courseStatus;
        return this;
    }
    
    /**
     * do sign in
     * @return
     */
    public CourseRound signin() {
        this.finished += 1;
        return this;
    }
    
    /**
     * 
     * @return
     */
    public CourseRound finishCourseRound() {
        this.courseStatus = RoundStatus.FINISH;
        this.finishTime = new Date();
        return this;
    }
    
    /**
     * 
     * @param name
     * @param value
     * @return
     */
    public CourseRound editAttribute(String name, Object value) {
        if(MapUtils.isEmpty(attrs)) {
            this.attrs = new HashMap<String, CourseRoundAttribute>();
            this.attrs.put(name, new CourseRoundAttribute(this, name, value.toString()));
        }else {
            CourseRoundAttribute courseRoundAttribute = this.attrs.get(name);
            courseRoundAttribute.setValue(value.toString());
            this.attrs.put(name, courseRoundAttribute);
        }
        return this;
    }
    
    /**
     * 
     * @param groupRoundAttr
     * @return
     */
    public CourseRound editGroupBasic(GroupRoundAttr groupRoundAttr) {
        
        if(this.groupAttr == null) {
            this.groupAttr = new GroupRoundAttr();
        }
        
        this.groupAttr.group(groupRoundAttr.getDescription(), groupRoundAttr.getGym(), groupRoundAttr.getOpenDescription());
        
        return this;
    }
    
    /**
     * 
     * @param bmiValue
     * @param zipName
     * @param qrcodeStatus
     * @return
     */
    public CourseRound editOnlineBasic(BigDecimal bmiValue, String zipName, boolean qrcodeStatus, OnlineRoundGroupStatus onlineRoundGroupStatus) {
        if(this.onlineRoundAttr == null) {
            this.onlineRoundAttr = new OnlineRoundAttr();
        }
        
        this.onlineRoundAttr.generate(bmiValue, zipName, qrcodeStatus, onlineRoundGroupStatus);
        
        return this;
    }
    
    /**
     * 
     * @param groupStatus
     * @return
     */
    public CourseRound editGroupStatus(OnlineRoundGroupStatus groupStatus) {
        this.onlineRoundAttr = this.onlineRoundAttr.editGroupStatus(groupStatus);
        return this;
    }
    
    /**
     * 
     * @return
     */
    public CourseRound editZipName() {
        this.onlineRoundAttr.editZipName(this.order);
        return this;
    }
    
    /**
     * 
     * @param wechatNumber
     * @return
     */
    public CourseRound editZipName(String wechatNumber) {
        this.onlineRoundAttr.editZipName(wechatNumber);
        return this;
    }
    
    /**
     * 
     * @param coach
     * @return
     */
    public CourseRound bindCoach(Principal coach) {
        this.coach = coach;
        return this;
    }
    
    public CourseRound freezeDay(Integer holdDays) {
        this.courseStatus = RoundStatus.HOLD;
        this.holdDate = new Date();
        this.freezedNumber = this.freezedNumber + 1;
        if(holdDays != null) {
            this.holdEndDate = org.apache.commons.lang3.time.DateUtils.addDays(this.holdDate, holdDays);
        }
        return this;
    }
    
    /**
     * 
     * @return
     */
    public CourseRound freeze(Integer holdMonth) {
        this.courseStatus = RoundStatus.HOLD;
        this.holdDate = new Date();
        this.freezedNumber = this.freezedNumber + 1;
        
        if(holdMonth != null) {
            this.holdEndDate = org.apache.commons.lang3.time.DateUtils.addMonths(this.holdDate, holdMonth);
        }
        
        if(this.fitnessGroup != null) {
            int week;
            
            Date effectiveStartDay = DateUtils.parseDate(DateFormatUtils.format(this.effectiveStart, "yyyy-MM-dd"), "yyyy-MM-dd");
            Date effectiveEndDay = this.getEffectiveEndDay();
            
            int day = DateUtils.daysBetween(effectiveStartDay, effectiveEndDay) + 1;
            
            if(day%7 == 0 || day%7 == 1) {
                week = day/7;
            } else {
                week = day/7 + 1;
            }
            
            if(MapUtils.isEmpty(attrs) || attrs.get(Constant.ONLINE_PLAN_AMOUNT) == null) {
                this.addAttribute(Constant.ONLINE_PLAN_AMOUNT, week);
            } else {
                this.editAttribute(Constant.ONLINE_PLAN_AMOUNT, week);
            }
        }
        
        return this;
    }
    
    /**
     * 
     * @return
     */
    public CourseRound unfreeze() {
        this.courseStatus = RoundStatus.READY;
        this.holdEndDate = new Date();
        
        if(this.fitnessGroup != null) {
            int week;
            
            Date effectiveStartDay = DateUtils.parseDate(DateFormatUtils.format(this.effectiveStart, "yyyy-MM-dd"), "yyyy-MM-dd");
            Date effectiveEndDay = DateUtils.parseDate(DateFormatUtils.format(this.effectiveEnd, "yyyy-MM-dd"), "yyyy-MM-dd");
            
            int day = DateUtils.daysBetween(effectiveStartDay, effectiveEndDay) + 1;
            
            if(day%7 == 0 || day%7 == 1) {
                week = day/7;
            } else {
                week = day/7 + 1;
            }
            
            if(MapUtils.isEmpty(attrs) || attrs.get(Constant.ONLINE_PLAN_AMOUNT) == null) {
                this.addAttribute(Constant.ONLINE_PLAN_AMOUNT, week);
            } else {
                this.editAttribute(Constant.ONLINE_PLAN_AMOUNT, week);
            }
        }
        
        return this;
    }
    
    /**
     * 
     * @param currentDate 
     * @param courseCode 
     * @param gym 
     * @param startDate 
     * @return
     */
    public CourseRound group(Date currentDate, String courseCode, String gym, String startDate) {
        
        this.code = courseType.getShortName() + site.getId().toString() + course.getId() + currentDate.getTime();
        
        if(this.groupAttr == null) {
            this.groupAttr = new GroupRoundAttr();
        }
        
        this.groupAttr = groupAttr.group(courseCode, gym, startDate);
        
        return this;
    }
    
    /**
     * @param name
     * @param value
     * @return
     */
    public CourseRound addAttribute(String name, Object value) {
        if(MapUtils.isEmpty(attrs)) {
            this.attrs = new HashMap<String, CourseRoundAttribute>();
        }
        this.attrs.put(name, new CourseRoundAttribute(this, name, value.toString()));
        return this;
    }
    
    /**
     * 
     * @param effectiveEnd
     * @return
     */
    public CourseRound editEffectiveEnd(Date effectiveEnd) {
        this.effectiveEnd = effectiveEnd;
        return this;
    }
    
    /**
     * edit effective end when toggle freeze
     * 
     * @return
     */
    public CourseRound editEffectiveEnd() {
        this.effectiveEnd = new Date(new Date().getTime() + this.effectiveEnd.getTime() - this.holdDate.getTime());
        return this;
    }
    
    /**
     * 
     * @return
     */
    public CourseRound readyCourse() {
        this.courseStatus = RoundStatus.READY;
        return this;
    }
    
    /**
     * 
     * @param fitnessGroup
     * @return
     */
    public CourseRound changeGroup(FitnessGroup fitnessGroup) {
        this.fitnessGroup = fitnessGroup;
        this.roundCoursewareStatus = RoundCoursewareStatus.EDITING;
        return this;
    }
    
    /**
     * 
     * @return
     */
    public CourseRound finishCourseware() {
        this.roundCoursewareStatus = RoundCoursewareStatus.FINISH;
        return this;
    }
    
    /**
     * 
     * @return
     */
    public CourseRound editingCourseware() {
        this.roundCoursewareStatus = RoundCoursewareStatus.EDITING;
        return this;
    }
    
    /**
     * 
     * @return
     */
    public CourseRound initPeriod(Date day) {

        if ((this.courseType.isIndividual() || this.courseType.equals(CourseType.SPECIAL) ||  this.courseType.equals(CourseType.POSTURE) || this.courseType.isYear()) && day != null) {
            this.effectiveStart = day;
        } else {
            this.effectiveStart = new Date();
        }

        if (this.courseType.isIndividual() && !this.course.isExperience()) {

            BigDecimal weeks = new BigDecimal(0);

            if (!MapUtils.isEmpty(attrs)) {
                CourseRoundAttribute courseRoundAttribute = attrs.get(Constant.COURSE_AMOUNT);
                if (courseRoundAttribute != null
                    && Integer.parseInt(courseRoundAttribute.getValue()) > this.course.getHours()) {
                    weeks =
                        new BigDecimal(courseRoundAttribute.getValue()).subtract(new BigDecimal(this.course.getHours()))
                            .multiply(new BigDecimal(0.5)).add(new BigDecimal(this.course.getExpires()));
                } else {
                    weeks = new BigDecimal(this.course.getExpires());
                }
            } else {
                weeks = new BigDecimal(this.course.getExpires());
            }

            this.effectiveEnd = org.apache.commons.lang3.time.DateUtils.addWeeks(this.effectiveStart, weeks.intValue());

            if (new BigDecimal(weeks.intValue()).compareTo(weeks) != 0) {
                this.effectiveEnd = org.apache.commons.lang3.time.DateUtils.addDays(this.effectiveEnd, 3);
            }

        } else if(this.courseType.isYear()) {
            this.effectiveEnd = org.apache.commons.lang3.time.DateUtils.addDays(this.effectiveStart, this.course.getExpires());
        } else {
            this.effectiveEnd =
                org.apache.commons.lang3.time.DateUtils.addWeeks(this.effectiveStart, this.course.getExpires());
        }

        return this;
    }
    
    /**
     * 
     * @param effectiveStart
     * @return
     */
    public Date calculateEffectiveEnd(Date effectiveStart) {
        Date effectiveEnd = null;
        
        if((this.courseType.isIndividual()||this.courseType.isSpecial()||this.courseType.isPosture()) && !this.course.isExperience()) {
            BigDecimal weeks = new BigDecimal(0);
            
            if(!MapUtils.isEmpty(attrs)) {
                CourseRoundAttribute courseRoundAttribute = attrs.get(Constant.COURSE_AMOUNT);
                if(courseRoundAttribute != null && Integer.parseInt(courseRoundAttribute.getValue()) > this.course.getHours()) {
                    weeks = new BigDecimal(courseRoundAttribute.getValue()).subtract(new BigDecimal(this.course.getHours()))
                                                                           .multiply(new BigDecimal(0.5))
                                                                           .add(new BigDecimal(this.course.getExpires()));
                } else {
                    weeks = new BigDecimal(this.course.getExpires());
                }
            } else {
                weeks = new BigDecimal(this.course.getExpires());
            }
            effectiveEnd = org.apache.commons.lang3.time.DateUtils.addWeeks(effectiveStart, weeks.intValue());
            
            if(new BigDecimal(weeks.intValue()).compareTo(weeks) != 0) {
                effectiveEnd = org.apache.commons.lang3.time.DateUtils.addDays(this.effectiveEnd, 3);
            }
            
        } else {
            effectiveEnd = org.apache.commons.lang3.time.DateUtils.addWeeks(effectiveStart, this.course.getExpires());
        }
        return effectiveEnd;
    }
    
    /**
     * 
     * @param achievementQrcode
     * @return
     */
    public CourseRound editAchievementQrcode(String achievementQrcode) {
        this.achievementQrcode = achievementQrcode;
        
        return this;
    }
    
    /**
     * 
     * @return
     */
    @Transient
    public long getExpiresDays() {
        int days = this.course.getWeeks() * 7;
        Date expireDate = org.apache.commons.lang3.time.DateUtils.addDays(this.effectiveStart, days);
        return (expireDate.getTime() - new Date().getTime())/(24 * 60 * 60 * 1000);
    }
    
    /**
     * 
     * @return
     */
    public long getEffectiveEndDays() {
        
        if(effectiveEnd == null) {
            return -1;
        } else {
            if(this.courseStatus.isHold()) {
                return (effectiveEnd.getTime() - holdDate.getTime())/(24 * 60 * 60 *1000);
            } else {
                return (effectiveEnd.getTime() - new Date().getTime())/(24 * 60 * 60 *1000);
            }
        }
    }
    
    /**
     * 
     * @return
     */
    public Date getEffectiveEndTime() {
        if(this.courseStatus.isHold() && this.effectiveEnd != null && this.holdDate != null) {
            return new Date(new Date().getTime() + this.effectiveEnd.getTime() - this.holdDate.getTime());
        } else {
            return effectiveEnd;
        }
    }
    
    /**
     * 
     * @return
     */
    @Transient
    public long getRoundTimes() {
        return this.effectiveEnd.getTime() - this.effectiveStart.getTime();
    }
    
    /**
     * 
     * @return
     */
    @Transient
    public Map<String, Integer> getEffectiveEndWeeks() {
        Map<String, Integer> result = new HashMap<String, Integer>();
        int k = 2;
        for(int i=12,j=1; i<109; i+=12,j++) {
            result.put(Integer.toString(i), i/2+k);
            if(j == 1 || j == 3 || j == 5 || j == 6 || j == 8) {
                k = k + 2;
            }
        }
        
        return result;
    }
    
    /**
     * 
     * @param coach
     * @return
     */
    public CourseRound bindGroupCoach(Principal coach) {
        this.coach = coach;
        return this;
    }
    
    /**
     * Sync course round by another course round
     * 
     * @param originalRound
     * @return
     */
    public CourseRound syncRound(CourseRound originalRound) {
        // 除了新建已经有的字段，其他的字段全部更新
        this.groupAttr = originalRound.getGroupAttr();
        this.code = originalRound.getCode();
        this.course = originalRound.getCourse();
        this.coach = originalRound.getCoach();
        this.courseType = originalRound.getCourseType();
        this.courseStatus = originalRound.getCourseStatus();
        this.finished = originalRound.getFinished();
        return this;
    }
    
    /**
     * 
     * @return
     */
    public CourseRound disabled() {
        this.disabled = true;
        return this;
    }
    
    /**
     * 
     * @param product
     * @param code
     * @return
     */
    public CourseRound transferCamp(Product product, String code) {
        this.product = product;
        this.course = product.getCourse();
        this.code = code;
        return this;
    }
    
    /**
     * 
     * @return
     */
    public CourseRound removeCourse() {
        this.code = null;
        return this;
    }
    
    /**
     * 
     * @param effectiveEnd
     * @return
     */
    public CourseRound delay(Date effectiveEnd) {
        if(courseStatus.isReady()) {
            this.effectiveEnd = effectiveEnd;
        } else if(courseStatus.isExpire() || this.courseStatus.isFinish() || this.courseStatus.isForeverexpire()) {
            this.effectiveEnd = effectiveEnd;
            this.courseStatus = RoundStatus.ACTIVATE;
        }
        
        if(this.fitnessGroup != null) {
            int week;
            
            Date effectiveStartDay = DateUtils.parseDate(DateFormatUtils.format(this.effectiveStart, "yyyy-MM-dd"), "yyyy-MM-dd");
            Date effectiveEndDay = DateUtils.parseDate(DateFormatUtils.format(this.effectiveEnd, "yyyy-MM-dd"), "yyyy-MM-dd");
            
            int day = DateUtils.daysBetween(effectiveStartDay, effectiveEndDay) + 1;
            
            if(day%7 == 0 || day%7 == 1) {
                week = day/7;
            } else {
                week = day/7 + 1;
            }
            
            if(MapUtils.isEmpty(attrs) || attrs.get(Constant.ONLINE_PLAN_AMOUNT) == null) {
                this.addAttribute(Constant.ONLINE_PLAN_AMOUNT, week);
            } else {
                this.editAttribute(Constant.ONLINE_PLAN_AMOUNT, week);
            }
        }
        
        return this;
    }
    
    /**
     * 
     * @return
     */
    public CourseRound activate() {
        int courseAmount = this.course.getHours();
        if(MapUtils.isNotEmpty(this.attrs) && this.attrs.get(Constant.COURSE_AMOUNT) != null) {
            courseAmount = Integer.parseInt(this.attrs.get(Constant.COURSE_AMOUNT).getValue());
        }
        
        if(this.finished == courseAmount) {
            this.courseStatus = RoundStatus.FINISH;
        } else {
            this.courseStatus = RoundStatus.READY;
        }
        
        return this;
    }
    
    /**
     * 
     * @return
     */
    public boolean couldChangeSite() {
        return this.courseStatus.isPrepare() || this.courseStatus.isReady() || this.courseStatus.isHold();
    }
    
    /**
     * 
     * @param type
     * @param amount
     * @return
     */
    public CourseRound editPlannedAmount(String type, int amount) {
        if(org.apache.commons.lang3.StringUtils.equals(type, "weekly")) {
            if(this.attrs != null && this.attrs.get(Constant.ONLINE_PLAN_AMOUNT) != null) {
                int weeklyAmount = Integer.parseInt(this.attrs.get(Constant.ONLINE_PLAN_AMOUNT).getValue());
                
                if(this.plannedWeekly < weeklyAmount && this.plannedWeekly + amount <= weeklyAmount) {
                    this.plannedWeekly = this.plannedWeekly + amount;
                } else {
                    this.plannedWeekly = weeklyAmount;
                }
            } else if(this.attrs != null && this.attrs.get("courseware_weekly_amount") != null) {
                int weeklyAmount = Integer.parseInt(this.attrs.get("courseware_weekly_amount").getValue());
                
                if(this.plannedWeekly < weeklyAmount && this.plannedWeekly + amount <= weeklyAmount) {
                    this.plannedWeekly = this.plannedWeekly + amount;
                } else {
                    this.plannedWeekly = weeklyAmount;
                }
            }
           
        } else {
            this.plannedCourse = this.plannedCourse + amount;
        }
        return this;
    }

    /**
     * 
     * @param plannedWeekly
     * @return
     */
    public CourseRound editPlannedWeekly(int plannedWeekly) {
        this.plannedWeekly = plannedWeekly;
        return this;
    }

    /**
     * 
     * @param plannedContent
     * @return
     */
    public CourseRound editPlannedContent(int plannedContent) {
        this.plannedContent = plannedContent;
        return this;
    }

    /**
     * 
     * @param plannedCourseware
     * @return
     */
    public CourseRound editPlannedCourseware(int plannedCourseware) {
        this.plannedCourseware = plannedCourseware;
        return this;
    }

    /**
     * 绑定主课程
     * @param mainCourseRound 主课程
     * @return
     */
    public CourseRound bindMain(CourseRound mainCourseRound) {
        this.mainCourseRound = mainCourseRound;
        return this;
    }

    /**
     * 解绑主课程
     * @return
     */
    public CourseRound unbindMain() {
        this.mainCourseRound = null;
        return this;
    }
    
    /**
     * 
     * @param fullIn
     * @return
     */
    public CourseRound fullInTarget(boolean fullIn) {
        this.fullInTarget  = fullIn;
        return this;
    }
    
    /**
     * 
     * @return
     */
    public boolean isFinish() {
        return this.courseStatus.equals(RoundStatus.FINISH);
    }
    
    /**
     * 
     * @return
     */
    @Transient
    public boolean isHold() {
        return this.courseStatus.equals(RoundStatus.HOLD);
    }
    
    /**
     * 
     * @return
     */
    @Transient
    public int getSurplusDays() {
        if(this.effectiveEnd != null) {
            long endDay = this.effectiveEnd.getTime();
            long today = System.currentTimeMillis();
            return  (int) ((endDay - today)/1000/60/60/24);
        }
        return 0;
    }
    
    /**
     * 
     * @return
     */
    @Transient
    public String getAppointStartTime() {
        if(StringUtils.isEmpty(this.course.getAppointStartTime())) {
            return this.site.getAppointStartHour();
        } else {
            Date courseAppoint = DateUtils.parseDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd") + " " + this.course.getAppointStartTime() + ":00", "yyyy-MM-dd HH:mm:ss");
            Date siteAppoint = DateUtils.parseDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd") + " " + this.site.getAppointStartHour() + ":00", "yyyy-MM-dd HH:mm:ss");
            return courseAppoint.after(siteAppoint) ? this.course.getAppointStartTime() : this.site.getAppointStartHour();
        }
    }
    
    /**
     * 
     * @return
     */
    @Transient
    public String getAppointEndTime() {
        
        Date siteAppoint = DateUtils.parseDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd") + " " + this.site.getAppointEndHour() + ":00", "yyyy-MM-dd HH:mm:ss");
        String siteAppointHour;
        if((this.courseType.isIndividual()||this.courseType.isSpecial()||this.courseType.isPosture()) && course.isExperience()) {
            siteAppointHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addMinutes(siteAppoint, -90), "HH:mm");
        } else {
            siteAppointHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(siteAppoint, -1), "HH:mm");
        }
        
        if(StringUtils.isEmpty(this.course.getAppointEndTime())) {
            return siteAppointHour;
        } else {
            Date courseAppoint = DateUtils.parseDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd") + " " + this.course.getAppointEndTime() + ":00", "yyyy-MM-dd HH:mm:ss");
            String courseAppointHour;
            if((this.courseType.isIndividual()||this.courseType.isSpecial()||this.courseType.isPosture()) && course.isExperience()) {
                courseAppointHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addMinutes(courseAppoint, -90), "HH:mm");
            } else {
                courseAppointHour = DateFormatUtils.format(org.apache.commons.lang3.time.DateUtils.addHours(courseAppoint, -1), "HH:mm");
            }
            
            return courseAppoint.before(siteAppoint) ? courseAppointHour : siteAppointHour;
        }
    }
    
    /**
     * @return
     */
    @Transient
    public String getEffectiveStartAsString() {
        
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        
        if (this.effectiveStart != null) {
            return sdf.format(this.effectiveStart);
        }
        
        return null;
        
    }
    
    @Transient
    public boolean isDishonesty() {
        if(courseType.isYear() && attrs != null && attrs.get("dishonesty") != null && "1".equals(attrs.get("dishonesty").getValue())) {
            return true;
        } else {
            return false;
        }
    }
    
    /**
     * 课时数
     * @return
     */
    public int getCourseAmount() {
        if(MapUtils.isNotEmpty(this.attrs) && this.attrs.get(Constant.COURSE_AMOUNT) != null) {
            return Integer.parseInt(this.attrs.get(Constant.COURSE_AMOUNT).getValue());
        } else {
            return this.course.getHours();
        }
    }

    /**
     * 是否可预约
     * @return
     */
    public boolean isAppointAvailable() {
        if(this.courseType.isIndividual()||this.courseType.isSpecial()||this.courseType.isPosture() || courseType.isYear()) {
            if(courseStatus.isPrepare() && coach != null) {
                return true;
            } else if(courseStatus.isReady()) {
                if(effectiveEnd == null || effectiveEnd.after(org.apache.commons.lang3.time.DateUtils.addMinutes(new Date(), course.isExperience() ? 90 : 60))) {
                    if(courseType.isYear()) {
                        return true;
                    } else {
                        for(CourseSchedule schedule : schedules) {
                            if(schedule.getScheduleStatus().isSchedule()) {
                                return true;
                            }
                        }
                    }
                }
            }
        }
        
        return false;
    }

    /**
     * 
     * @return
     */
    public boolean isTargetNeeded() {
        if((this.courseType.isIndividual()||this.courseType.isSpecial()||this.courseType.isPosture() || courseType.isYear()) && !course.isExperience()) {
            if(courseStatus.isPrepare() && (fullInTarget == null || fullInTarget)) {
                return true;
            }
        }
        
        return false;
    }

    /**
     * 
     * @return
     */
    @Transient
    public String getTransferId() {
        return Long.toString(this.id);
    }

    /**
     * 
     * @return
     */
    @Transient
    public Date getEffectiveEndDay() {
        if(effectiveEnd != null) {
            if(courseStatus.isHold()) {
                Date unfreezeTime = DateUtils.parseDate(DateFormatUtils.format(holdEndDate, "yyyy-MM-dd") + " 00:30:00", "yyyy-MM-dd HH:mm:ss");
                if(!holdEndDate.before(unfreezeTime)) {
                    unfreezeTime = DateUtils.addDays(unfreezeTime, 1);
                }
                
                return DateUtils.parseDate(DateFormatUtils.format(new Date(unfreezeTime.getTime() + effectiveEnd.getTime() - holdDate.getTime()), "yyyy-MM-dd"), "yyyy-MM-dd");
            } else {
                return DateUtils.parseDate(DateFormatUtils.format(effectiveEnd, "yyyy-MM-dd"), "yyyy-MM-dd");
            }
        } else {
            return null;
        }
    }
    
    @Transient
    public String getCategoryName() {
        if(this.courseType.equals(CourseType.YEAR)) {
            return "周期课";
        } else if(this.courseType.equals(CourseType.POSTURE)) {
            return "专项课";
        } else if(this.courseType.equals(CourseType.SPECIAL)) {
            return "特色课";
        } else if(this.course.isExperience()) {
            return "体验课";
        } else if(this.course.isGive()) {
            return "赠课";
        } else {
            return "常规课";
        }
    }

    /**
     * 课程封面
     * @return
     */
    public String getCourseCover() {
        return course.getCover();
    }

    /**
     * 课程名称
     * @return
     */
    public String getCourseName() {
        return course.getName();
    }

    /**
     * 门店名称
     * @return
     */
    public String getSiteName() {
        return site == null ? null : site.getName();
    }
    /**
     * 教练名称
     * @return
     */
    public String getCoachName() {
        return coach == null ? null : coach.getName();
    }

    /**
     * 产品名称
     * @return
     */
    public String getProductName() {
        return product == null ? null : product.getName();
    }

    /**
     * 学员头像
     * @return
     */
    public String getStudentAvatar() {
        return student.getUser().getProfile() == null ? null : student.getUser().getProfile().getAvatar();
    }

    /**
     * 学员姓名
     * @return
     */
    public String getStudentName() {
        return student.getName();
    }

    /**
     * 学员手机号
     * @return
     */
    public String getStudentMobile() {
        return student.getMobile();
    }

    /**
     * 分组名称
     * @return
     */
    public String getFitnessGroupName() {
        return fitnessGroup == null ? null : fitnessGroup.getName();
    }

    /**
     * 总周计划数
     * @return
     */
    public int getFitnessGroupWeek() {
        return fitnessGroup == null ? 0 : fitnessGroup.getWeeks();
    }

    /**
     * 是否赠课
     * @return
     */
    public boolean isGive() {
        return course.isGive();
    }
    
    public Boolean getLastCourse() {
        return lastCourse;
    }

    /**
     * 
     * @return
     */
    public boolean isGiveFinished() {
        if(this.giveCourseRound != null) {
            if(this.giveCourseRound.isFinish() || this.giveCourseRound.getCourseStatus().isCancel()) {
                return true;
            } else if(this.giveCourseRound.getCourseStatus().isPrepare()) {
                return this.giveCourseRound.getCourseAmount() == 0 ? true : false;
            } else {
                List<CourseSchedule> schedules = this.giveCourseRound.getSchedules();
                
                int rest = 0;
                if(schedules != null && !schedules.isEmpty()) {
                    for(CourseSchedule schedule : schedules) {
                        if(schedule.getScheduleStatus().isSchedule() || schedule.getScheduleStatus().isExpiry()) {
                            return false;
                        } else if(schedule.getScheduleStatus().isReady()) {
                            rest++;
                        }
                    }
                }
                
                return rest > 2 ? false : true;
            }
        } else {
            return true;
        }
    }

    /**
     * 检查上周、本周及下周线上计划是否完成
     * @return
     */
    @Transient
    public String getWeeklyFinished() {
        if(this.courseStatus.isPrepare()) {
            if(this.batches == null || this.batches.size() < 1 || !this.batches.get(0).getEditStatus().isGenerated()) {
                return "NO";
            } else {
                return "YES";
            }
        } else if(this.courseStatus.isReady() || this.courseStatus.isHold() || this.courseStatus.isActivate()) {
            if(this.effectiveStart != null) {
                Date effectiveStartDay = DateUtils.parseDate(DateFormatUtils.format(this.effectiveStart, "yyyy-MM-dd"), "yyyy-MM-dd");
                Date today = DateUtils.parseDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd"), "yyyy-MM-dd");
                
                if(today.before(effectiveStartDay)) {
                    if(this.batches == null || this.batches.size() < 1 || !this.batches.get(0).getEditStatus().isGenerated()) {
                        return "NO";
                    } else {
                        return "YES";
                    }
                } else {
                    int thisWeek = DateUtils.daysBetween(effectiveStartDay, today)/7 + 1;
                    int effectieWeek = this.getEffectiveWeek();
                    
                    if(thisWeek >= effectieWeek) {
                        if(this.batches == null || this.batches.size() < effectieWeek
                                || !this.batches.get(effectieWeek - 2).getEditStatus().isGenerated()
                                || !this.batches.get(effectieWeek - 1).getEditStatus().isGenerated()) {
                            return "NO";
                        } else {
                            return "YES";
                        }
                    } else {
                        if(this.batches == null || this.batches.size() < effectieWeek
                                || !this.batches.get(thisWeek - 2).getEditStatus().isGenerated()
                                || !this.batches.get(thisWeek - 1).getEditStatus().isGenerated()
                                || !this.batches.get(thisWeek).getEditStatus().isGenerated()) {
                            return "NO";
                        } else {
                            return "YES";
                        }
                    }
                }
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    /**
     * 最后上课日期
     * @return
     */
    @Transient
    public Date getLatestScheduleDate() {
        if(this.schedules != null && !this.schedules.isEmpty()) {
            for(int i=this.schedules.size()-1; i>=0; i--) {
                if(this.schedules.get(i).getScheduleStatus().isFinish()) {
                    return this.schedules.get(i).getOccurDate();
                }
            }
        }
        
        return null;
    }

    /**
     * 提醒教练完成本周及后两周线上计划
     * @return
     */
    @Transient
    public String getRemain() {
        if(this.courseStatus.isPrepare() || this.courseStatus.isReady() || this.courseStatus.isHold() || this.courseStatus.isActivate()) {
            Date effectiveStartDay = null;
            if(this.effectiveStart != null) {
                effectiveStartDay = DateUtils.parseDate(DateFormatUtils.format(this.effectiveStart, "yyyy-MM-dd"), "yyyy-MM-dd");
            }
            Date today = DateUtils.parseDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd"), "yyyy-MM-dd");
            int weeks = this.getEffectiveWeek();
            
            List<Integer> remainWeeks = new ArrayList<>();
            if(this.effectiveStart == null || this.effectiveEnd == null || today.before(effectiveStartDay)) {
                if(weeks == 1 && (this.batches == null || this.batches.isEmpty() || !batches.get(0).getEditStatus().isGenerated())) {
                    remainWeeks.add(1);
                } else if(weeks >= 2) {
                    if(this.batches == null || this.batches.size() < 2) {
                        remainWeeks.add(1);
                        remainWeeks.add(2);
                    } else {
                        if(!batches.get(0).getEditStatus().isGenerated()) {
                            remainWeeks.add(1);
                        }
                        if(!batches.get(1).getEditStatus().isGenerated()) {
                            remainWeeks.add(2);
                        }
                    }
                }
            } else {
                int day = DateUtils.daysBetween(effectiveStartDay, today);
                int thisWeek = day/7 + 1;
                
                if(thisWeek >= weeks && (this.batches == null || this.batches.size() < weeks
                        || !batches.get(weeks -1).getEditStatus().isGenerated())) {
                    remainWeeks.add(weeks);
                } else if(thisWeek == weeks - 1) {
                    if(this.batches == null || this.batches.size() < weeks) {
                        remainWeeks.add(thisWeek);
                        remainWeeks.add(thisWeek + 1);
                    } else {
                        if(!batches.get(thisWeek -1).getEditStatus().isGenerated()) {
                            remainWeeks.add(thisWeek);
                        }
                        if(!batches.get(thisWeek).getEditStatus().isGenerated()) {
                            remainWeeks.add(thisWeek + 1);
                        }
                    }
                } else if(thisWeek < weeks - 1) {
                    if(this.batches == null || this.batches.size() < thisWeek + 2) {
                        remainWeeks.add(thisWeek);
                        remainWeeks.add(thisWeek + 1);
                        remainWeeks.add(thisWeek + 2);
                    } else {
                        if(!batches.get(thisWeek -1).getEditStatus().isGenerated()) {
                            remainWeeks.add(thisWeek);
                        }
                        if(!batches.get(thisWeek).getEditStatus().isGenerated()) {
                            remainWeeks.add(thisWeek + 1);
                        }
                        if(!batches.get(thisWeek + 1).getEditStatus().isGenerated()) {
                            remainWeeks.add(thisWeek + 2);
                        }
                    }
                }
            }
            
            if(!remainWeeks.isEmpty()) {
                StringBuffer buffer = new StringBuffer("请尽快安排第" + remainWeeks.get(0));
                for(int i=1; i<remainWeeks.size(); i++) {
                    buffer.append("," + remainWeeks.get(i));
                }
                buffer.append("周线上计划");
                
                return buffer.toString();
            }
        }
        
        return null;
    }

    /** 线上计划周数 */
    @Transient
    public int getEffectiveWeek() {
        if(courseStatus.isFinish() || courseStatus.isCancel()) {
            return fitnessGroup == null ? 0 : fitnessGroup.getWeeks();
        } else if(!MapUtils.isEmpty(attrs) && attrs.get(Constant.ONLINE_PLAN_AMOUNT) != null) {
            return Integer.parseInt(attrs.get(Constant.ONLINE_PLAN_AMOUNT).getValue());
        } else if(this.effectiveStart == null || this.effectiveEnd == null) {
            if((this.courseType.isIndividual()||this.courseType.isSpecial()||this.courseType.isPosture()) && !this.course.isExperience() && !MapUtils.isEmpty(attrs)) {
                CourseRoundAttribute courseRoundAttribute = attrs.get(Constant.COURSE_AMOUNT);
                
                if(courseRoundAttribute != null && Integer.parseInt(courseRoundAttribute.getValue()) > this.course.getHours()) {
                    BigDecimal weeks = new BigDecimal(courseRoundAttribute.getValue()).subtract(new BigDecimal(this.course.getHours()))
                                                                                      .multiply(new BigDecimal(0.5))
                                                                                      .add(new BigDecimal(this.course.getExpires()));
                    int intWeeks = weeks.intValue();
                    if(new BigDecimal(intWeeks).compareTo(weeks) != 0) {
                        return intWeeks + 1;
                    } else {
                        return intWeeks;
                    }
                }
            }
            
            return this.course.getExpires();
        } else {
            Date effectiveStartDay = DateUtils.parseDate(DateFormatUtils.format(this.effectiveStart, "yyyy-MM-dd"), "yyyy-MM-dd");
            Date effectiveEndDay = this.getEffectiveEndDay();
            
            int day = DateUtils.daysBetween(effectiveStartDay, effectiveEndDay) + 1;
            
            if(day%7 == 0 || day%7 == 1) {
                return day/7;
            } else {
                return day/7 + 1;
            }
        }
    }

    /**
     * @return the id
     */
    public Long getId() {
        return id;
    }
    
    public String getIdString() {
    	return this.id.toString();
    }

    /**
     * @return the code
     */
    public String getCode() {
        return code;
    }

    /**
     * @return the course
     */
    public Course getCourse() {
        return course;
    }

    /**
     * @return the courseType
     */
    public CourseType getCourseType() {
        return courseType;
    }
    
    public CourseUnit getCourseUnit() {
        return courseUnit;
    }

    /**
     * @return the site
     */
    public Site getSite() {
        return site;
    }

    /**
     * @return the product
     */
    public Product getProduct() {
        return product;
    }

    /**
     * @return the order
     */
    public Order getOrder() {
        return order;
    }

    /**
     * @return the student
     */
    public Student getStudent() {
        return student;
    }

    /**
     * @return the coach
     */
    public Principal getCoach() {
        return coach;
    }

    /**
     * @return the fitnessGroup
     */
    public FitnessGroup getFitnessGroup() {
        return fitnessGroup;
    }

    /**
     * @return the roundCoursewareStatus
     */
    public RoundCoursewareStatus getRoundCoursewareStatus() {
        return roundCoursewareStatus;
    }

    /**
     * @return the createTime
     */
    public Date getCreateTime() {
        return createTime;
    }

    /**
     * @return the effectiveStart
     */
    public Date getEffectiveStart() {
        return effectiveStart;
    }

    /**
     * @return the effectiveEnd
     */
    public Date getEffectiveEnd() {
        return effectiveEnd;
    }

    /**
     * @return the holdDate
     */
    public Date getHoldDate() {
        return holdDate;
    }

    /**
     * @return the holdEndDate
     */
    public Date getHoldEndDate() {
        return holdEndDate;
    }

    /**
     * @return the finishTime
     */
    public Date getFinishTime() {
        return finishTime;
    }

    /**
     * @return the courseStatus
     */
    public RoundStatus getCourseStatus() {
        return courseStatus;
    }

    /**
     * @return the finished
     */
    public int getFinished() {
        return finished;
    }

    /**
     * @return the plannedCourse
     */
    public int getPlannedCourse() {
        return plannedCourse;
    }

    /**
     * @return the plannedWeekly
     */
    public int getPlannedWeekly() {
        return plannedWeekly;
    }

    /**
     * @return the plannedContent
     */
    public int getPlannedContent() {
        return plannedContent;
    }

    /**
     * @return the plannedCourseware
     */
    public int getPlannedCourseware() {
        return plannedCourseware;
    }

    /**
     * @return the freezedNumber
     */
    public int getFreezedNumber() {
        return freezedNumber;
    }

    /**
     * @return the given
     */
    public boolean isGiven() {
        return given;
    }

    /**
     * @return the groupAttr
     */
    public GroupRoundAttr getGroupAttr() {
        return groupAttr;
    }

    /**
     * @return the onlineRoundAttr
     */
    public OnlineRoundAttr getOnlineRoundAttr() {
        return onlineRoundAttr;
    }

    /**
     * @return the schedules
     */
    public List<CourseSchedule> getSchedules() {
        return schedules;
    }

    /**
     * @return the batches
     */
    public List<BatchCourseware> getBatches() {
        return batches;
    }

    /**
     * @return the attrs
     */
    public Map<String, CourseRoundAttribute> getAttrs() {
        return attrs;
    }

    /**
     * @return the disabled
     */
    public boolean isDisabled() {
        return disabled;
    }

    /**
     * @return the fullInTarget
     */
    public boolean isFullInTarget() {
        return fullInTarget == null ? false : fullInTarget;
    }

    /**
     * @return the fullInTarget
     */
    public Boolean getFullInTarget() {
        return fullInTarget;
    }

    /**
     * @return the achievementQrcode
     */
    public String getAchievementQrcode() {
        return achievementQrcode;
    }

    /**
     * @return the mainCourseRound
     */
    public CourseRound getMainCourseRound() {
        return mainCourseRound;
    }

    /**
     * @return the giveCourseRound
     */
    public CourseRound getGiveCourseRound() {
        return giveCourseRound;
    }

    /**
     * @param id the id to set
     */
    public void setId(Long id) {
        this.id = id;
    }

    /**
     * @param code the code to set
     */
    public void setCode(String code) {
        this.code = code;
    }

    /**
     * @param course the course to set
     */
    public void setCourse(Course course) {
        this.course = course;
    }

    /**
     * @param courseType the courseType to set
     */
    public void setCourseType(CourseType courseType) {
        this.courseType = courseType;
    }
    
    public void setCourseUnit(CourseUnit courseUnit) {
        this.courseUnit = courseUnit;
    }

    /**
     * @param site the site to set
     */
    public void setSite(Site site) {
        this.site = site;
    }

    /**
     * @param product the product to set
     */
    public void setProduct(Product product) {
        this.product = product;
    }

    /**
     * @param order the order to set
     */
    public void setOrder(Order order) {
        this.order = order;
    }

    /**
     * @param student the student to set
     */
    public void setStudent(Student student) {
        this.student = student;
    }

    /**
     * @param coach the coach to set
     */
    public void setCoach(Principal coach) {
        this.coach = coach;
    }

    /**
     * @param fitnessGroup the fitnessGroup to set
     */
    public void setFitnessGroup(FitnessGroup fitnessGroup) {
        this.fitnessGroup = fitnessGroup;
    }

    /**
     * @param roundCoursewareStatus the roundCoursewareStatus to set
     */
    public void setRoundCoursewareStatus(RoundCoursewareStatus roundCoursewareStatus) {
        this.roundCoursewareStatus = roundCoursewareStatus;
    }

    /**
     * @param createTime the createTime to set
     */
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }

    /**
     * @param effectiveStart the effectiveStart to set
     */
    public void setEffectiveStart(Date effectiveStart) {
        this.effectiveStart = effectiveStart;
    }

    /**
     * @param effectiveEnd the effectiveEnd to set
     */
    public void setEffectiveEnd(Date effectiveEnd) {
        this.effectiveEnd = effectiveEnd;
    }

    /**
     * @param holdDate the holdDate to set
     */
    public void setHoldDate(Date holdDate) {
        this.holdDate = holdDate;
    }

    /**
     * @param holdEndDate the holdEndDate to set
     */
    public void setHoldEndDate(Date holdEndDate) {
        this.holdEndDate = holdEndDate;
    }

    /**
     * @param finishTime the finishTime to set
     */
    public void setFinishTime(Date finishTime) {
        this.finishTime = finishTime;
    }

    /**
     * @param courseStatus the courseStatus to set
     */
    public void setCourseStatus(RoundStatus courseStatus) {
        this.courseStatus = courseStatus;
    }

    /**
     * @param finished the finished to set
     */
    public void setFinished(int finished) {
        this.finished = finished;
    }

    /**
     * @param plannedCourse the plannedCourse to set
     */
    public void setPlannedCourse(int plannedCourse) {
        this.plannedCourse = plannedCourse;
    }

    /**
     * @param plannedWeekly the plannedWeekly to set
     */
    public void setPlannedWeekly(int plannedWeekly) {
        this.plannedWeekly = plannedWeekly;
    }

    /**
     * @param plannedContent the plannedContent to set
     */
    public void setPlannedContent(int plannedContent) {
        this.plannedContent = plannedContent;
    }

    /**
     * @param plannedCourseware the plannedCourseware to set
     */
    public void setPlannedCourseware(int plannedCourseware) {
        this.plannedCourseware = plannedCourseware;
    }

    /**
     * @param freezedNumber the freezedNumber to set
     */
    public void setFreezedNumber(int freezedNumber) {
        this.freezedNumber = freezedNumber;
    }

    /**
     * @param given the given to set
     */
    public void setGiven(boolean given) {
        this.given = given;
    }
    
    public void setLastCourse(Boolean lastCourse) {
        this.lastCourse = lastCourse;
    }

    /**
     * @param groupAttr the groupAttr to set
     */
    public void setGroupAttr(GroupRoundAttr groupAttr) {
        this.groupAttr = groupAttr;
    }

    /**
     * @param onlineRoundAttr the onlineRoundAttr to set
     */
    public void setOnlineRoundAttr(OnlineRoundAttr onlineRoundAttr) {
        this.onlineRoundAttr = onlineRoundAttr;
    }

    /**
     * @param schedules the schedules to set
     */
    public void setSchedules(List<CourseSchedule> schedules) {
        this.schedules = schedules;
    }

    /**
     * @param batches the batches to set
     */
    public void setBatches(List<BatchCourseware> batches) {
        this.batches = batches;
    }

    /**
     * @param attrs the attrs to set
     */
    public void setAttrs(Map<String, CourseRoundAttribute> attrs) {
        this.attrs = attrs;
    }

    /**
     * @param disabled the disabled to set
     */
    public void setDisabled(boolean disabled) {
        this.disabled = disabled;
    }

    /**
     * @param fullInTarget the fullInTarget to set
     */
    public void setFullInTarget(boolean fullInTarget) {
        this.fullInTarget = fullInTarget;
    }

    /**
     * @param achievementQrcode the achievementQrcode to set
     */
    public void setAchievementQrcode(String achievementQrcode) {
        this.achievementQrcode = achievementQrcode;
    }

    /**
     * @param mainCourseRound the mainCourseRound to set
     */
    public void setMainCourseRound(CourseRound mainCourseRound) {
        this.mainCourseRound = mainCourseRound;
    }

    /**
     * @param giveCourseRound the giveCourseRound to set
     */
    public void setGiveCourseRound(CourseRound giveCourseRound) {
        this.giveCourseRound = giveCourseRound;
    }
    
    /**
	 * @return the courseTags
	 */
	public String getCourseTags() {
		return courseTags;
	}
	
	/**
	 * @param courseTags the courseTags to set
	 */
	public void setCourseTags(String courseTags) {
		this.courseTags = courseTags;
	}
}
